package com.feather.common.web;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.alibaba.fastjson.JSON;
import com.feather.common.data.DataColumn;
import com.feather.common.data.DataRowCollection;
import com.feather.common.data.DataSet;
import com.feather.common.data.DataTable;
import com.feather.common.data.DataTableCollection;

public class DataTableMapRule {
	public String			struct;	// { struct:"multi/single" }
	public List<Extra>		extra;
	public List<String>		remove;	// { remove:["dic_id", "layer_id"] }
	public List<String>		integer;	// { integer:["total", "length"] }
	public List<String>		floats;	// { floats:["price", "percent"] }
	public Map<String, ?>	append;	// { append:{ "customAttr":"new_info" }
										// }
	public Concat			concat;

	public static Object getMapObject(DataSet dataSet, String outColumnName) {
		if (dataSet != null) {
			DataTableCollection tables = dataSet.getResult().getTables();
			int tablesCount = tables.getCount();
			if (tablesCount > 0) {
				DataColumn mapRuleDC = dataSet.getResult().getColumns().get(outColumnName);
				if (mapRuleDC != null) {
					List<DataTableMapRule> ruleList = DataTableMapRule.parse(mapRuleDC.getString());
					if (ruleList != null) {
						return mapObject(tables, tablesCount, ruleList, 0, tables.get(0), null, null);
					}
					if (tablesCount == 1) {
						List<Map<String, ?>> list = tables.get(0).getRows().toMap();
						return list.size() == 1 ? list.get(0) : list;
					}
				}
			}
		}
		return dataSet;
	}

	public static List<DataTableMapRule> parse(String ruleText) {
		List<DataTableMapRule> list = null;
		if (ruleText != null && ruleText.length() > 0) {
			list = JSON.parseArray(ruleText, DataTableMapRule.class);
		}
		return list != null && list.size() > 0 ? list : null;
	}

	public boolean multi() {
		return "multi".equalsIgnoreCase(this.struct);
	}

	public boolean single() {
		return "single".equalsIgnoreCase(this.struct);
	}

	public void trim(Map<String, ?> line) {
		if (line != null) {
			remove(line);
			integer(line);
			floats(line);
			append(line);
		}
	}

	private void remove(Map<String, ?> map) {
		if (this.remove != null) {
			for (String name : this.remove) {
				Iterator<String> it = map.keySet().iterator();
				while (it.hasNext()) {
					String key = it.next();
					if (name.equalsIgnoreCase(key))
						it.remove();
				}
			}
		}
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void integer(Map map) {
		if (this.integer != null) {
			for (String name : this.integer) {
				if (map.containsKey(name)) {
					try {
						map.put(name, Long.parseLong((String) map.get(name), 10));
					} catch (NumberFormatException ex) {
						map.put(name, 0L);
					}
				}
			}
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void floats(Map map) {
		if (this.floats != null) {
			for (String name : this.floats) {
				if (map.containsKey(name)) {
					try {
						map.put(name, Double.parseDouble((String) map.get(name)));
					} catch (Exception ex) {
						map.put(name, 0D);
					}
				}
			}
		}
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void append(Map map) {
		if (this.append != null)
			map.putAll(this.append);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static Object mapObject(DataTableCollection tables, int tablesCount, List<DataTableMapRule> ruleList, int currentIndex, DataTable currentTable, Map<String, ?> parentLine, DataTableMapRule parentRule) {
		DataRowCollection rows = currentTable.getRows();
		List<Map<String, ?>> dataList = rows.toMap();
		DataTableMapRule rule = currentIndex > -1 && currentIndex < ruleList.size() ? ruleList.get(currentIndex) : null;
		if (rule == null) {
			return dataList.size() > 1 ? dataList : dataList.get(0);
		}
		List<Map<String, ?>> newList = tablesCount > 1 ? new ArrayList<Map<String, ?>>() : dataList;
		for (Map line : dataList) {
			if (parentRule == null || Extra.where(parentRule.extra, parentLine, currentIndex, line)) {
				Map newLine = null;
				if (tablesCount > 1) {
					newLine = new LinkedHashMap();
					newLine.putAll(line);
					newList.add(newLine);
				} else {
					newLine = line;
				}
				if (rule.extra != null) {
					for (Extra aExtra : rule.extra) {
						if (aExtra.name != null && aExtra.name.length() > 0) {
							DataTable extratable = tables.get(aExtra.index);
							newLine.put(aExtra.name, mapObject(tables, tablesCount, ruleList, aExtra.index, extratable, newLine, rule));
						}
					}
				}
				rule.trim(newLine);
			}
		}

		if (rule.single() || (newList.size() < 2 && !rule.multi())) {
			Map<String, ?> line = newList.size() > 0 ? newList.get(0) : null;
			return rule.concat != null ? rule.concat.concat(line) : line;
		}
		return rule.concat != null ? rule.concat.concat(newList) : newList;
	}

	public static class Extra {
		// { extra:[{ index:1, name:"rows", where:"parent_id=current_id" }] }
		public int			index;
		public String		name;
		public String		where;
		private String[]	whereFields;

		public String[] getWhereField() {
			if (this.whereFields == null) {
				if (this.where != null) {
					this.whereFields = this.where.split("=");
					for (int i = 0; i < this.whereFields.length; i++)
						this.whereFields[i] = this.whereFields[i].trim();
				}
			}
			return this.whereFields;
		}

		public static boolean where(List<Extra> extra, Map<String, ?> parentLine, int currentIndex, Map<String, ?> currentLine) {
			for (Extra aExtra : extra) {
				if (aExtra.index == currentIndex) {
					String[] fields = aExtra.getWhereField();
					if (fields == null || fields.length < 2) {
						return true;
					}
					Object parentValue = parentLine.get(fields[0]);
					return parentValue != null && parentValue.equals(currentLine.get(fields[1]));
				}
			}
			return true;
		}
	}

	public static class Concat {
		// { concat:{ field:"category", separator:";", format:"xxxxx"} }
		public static final String	FORMAT_ARRAY	= "array";

		public String				field;
		public String				separator;
		public String				format;

		public Object concat(Map<String, ?> map) {
			if (FORMAT_ARRAY.equalsIgnoreCase(this.format)) {
				return array(map);
			}
			if (this.field != null) {
				StringBuilder builder = new StringBuilder();
				if (map != null) {
					builder.append(map.get(this.field));
				}
				return builder;
			}
			return map;
		}

		public Object concat(List<Map<String, ?>> list) {
			if (FORMAT_ARRAY.equalsIgnoreCase(this.format)) {
				return array(list);
			}
			if (this.field != null) {
				StringBuilder builder = new StringBuilder();
				if (list != null) {
					String seqStr = this.separator != null ? this.separator : "";
					for (Map<String, ?> map : list) {
						if (builder.length() > 0) {
							builder.append(seqStr);
						}
						builder.append(map.get(this.field));
					}
				}
				return builder;
			}
			return list;
		}

		private Object array(Map<String, ?> map) {
			if (this.field != null) {
				List<Object> arr = new ArrayList<Object>();
				if (map != null) {
					arr.add(map.get(this.field));
				}
				return arr;
			}
			return map;
		}

		private Object array(List<Map<String, ?>> list) {
			if (this.field != null) {
				List<Object> arr = new ArrayList<Object>();
				if (list != null) {
					for (Map<String, ?> map : list)
						arr.add(map.get(this.field));
				}
				return arr;
			}
			return list;
		}
	}
}
